package com.haiertvbic.tvnow.protocol;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.ArrayList;

import com.haiertvbic.tvnow.model.ChannelPackage;
import com.haiertvbic.tvnow.model.LocalCode;
import com.haiertvbic.tvnow.model.TVChannel;
import com.haiertvbic.tvnow.util.SLog;

public class SocketPackage {
	private static final String TAG = "SocketPackage";
	private static SocketPackage socketPackte = null;
	private Socket socket;
	private DataOutputStream dos = null;
	private DataInputStream dis = null;

	int responseCode = 0;
	// 地域信息的成功返回码
	private final int LOCALINFO_RETURN_CODE = 4006300;

	// 获取当前频道号的成功返回码
	private static final int GET_CURRENT_CHANNEL_SUC = 4004900;

	/**
	 * 创建socketPackte对象
	 * 
	 * @return socketPackte
	 */
	public static SocketPackage getInstance() {
		if (null == socketPackte) {
			socketPackte = new SocketPackage();
		}
		return socketPackte;
	}

	/**
	 * 创建连接的方法，用于测试接口是否正确
	 * 
	 * @param ipString
	 *            主机名
	 * @param port
	 *            端口号
	 * @return
	 * @throws IOException
	 * @throws UnknownHostException
	 */
	public boolean createShortSocket(String ipString, int port) {
		try {
			if (socket != null && !socket.isClosed()) {
				socket.close();
			}
			socket = new Socket(ipString, port);
			socket.setSoTimeout(1000 * 30);
			if (socket != null) {
				dos = new DataOutputStream(socket.getOutputStream());
				dis = new DataInputStream(socket.getInputStream());
				return true;
			} else {
				return false;
			}
		} catch (IOException e) {
			SLog.i(TAG, e.toString());
			return false;
		}

	}

	public void closeSocket() {
		SLog.d(TAG, "close socket method");
		if (null != socket) {
			try {
				socket.close();
			} catch (IOException e) {
				SLog.i(TAG, e.toString());
			}
		}
		if (dis != null) {
			try {
				dis.close();
			} catch (Exception e) {
				SLog.i(TAG, e.toString());
			}
		}
		if (dos != null) {
			try {
				dos.close();
			} catch (Exception e) {
				SLog.i(TAG, e.toString());
			}
		}

	}

	/**
	 * 向CAE发送请求消息
	 * 
	 * @param cmd_ID
	 *            消息的id 4位
	 * @param msgnum
	 *            消息的序号，可以为0 4位
	 * @param data
	 *            数据体
	 * @throws IOException
	 */
	public void sendPacket(int cmd_ID, int msgnum, ByteBuffer data)
			throws IOException {

		int packageLength = data.array().length;
		ByteBuffer byteBuffer = ByteBuffer.allocate(16 + packageLength);

		// 将id放入流
		byte[] idByte = new byte[4];
		idByte = ConnectManager.intToByte(cmd_ID);
		byteBuffer.put(idByte);

		// 将消息序号放入流
		byte[] msgnumByte = new byte[4];
		msgnumByte = ConnectManager.intToByte(msgnum);
		byteBuffer.put(msgnumByte);

		// 将保留位放入流
		byte[] reserveByte = new byte[4];
		byteBuffer.put(reserveByte);

		// 将数据长度放入流
		byte[] packagelength = new byte[4];
		packagelength = ConnectManager.intToByte(packageLength);
		byteBuffer.put(packagelength);

		byteBuffer.put(data.array());
		byte[] sb = byteBuffer.array();
		SLog.d(TAG, "socketpackte.sendpacket.send");
		dos.write(sb);
		byteBuffer.clear();
		dos.flush();
		SLog.d(TAG, "socketpackte.sendpacket.send finish");
	}

	/**
	 * 解析地域信息<br>
	 * 
	 * @return LocalCodeBean 地域信息对象<br>
	 * @throws Exception
	 */
	/**
	 * 若返回码为4006300 成功,否则4006301 失败
	 * 
	 * @param cmdCaeAppGetLocalCodeRe
	 * 
	 * @return 地域信息对象
	 */
	public LocalCode getDeviceLocalInfo(int cmdCaeAppGetLocalCodeRe) {
		LocalCode LocalInfo = null;
		try {
			// 解析包头的消息ID,根据这个id来判断是哪一个消息的返回值，并且解析响应的数据
			if (recevicePacket(cmdCaeAppGetLocalCodeRe)) {
				LocalInfo = new LocalCode();
				// 返回码
				responseCode = dis.readInt();
				LocalInfo.setReturnCode(responseCode);
				SLog.d(TAG, "getDeviceLocalInfo.return_code == " + responseCode);
				// 如果4006300就成功
				if (responseCode == LOCALINFO_RETURN_CODE) {
					// 读取mac
					byte[] byte32 = new byte[32];
					dis.read(byte32);
					LocalInfo.setMac(new String(byte32, 0, byte32.length - 1));
					SLog.d(TAG,
							"getDeviceLocalInfo.setMac == "
									+ LocalInfo.getMac());
					byte[] byte32_user = new byte[32];
					int len_user = dis.read(byte32_user);
					String count32 = new String(byte32_user, 0, len_user);
					int index = 0;
					for (int j = 0; j < count32.length(); j++) {
						if (0 == (count32.charAt(j))) {
							index = j;
							break;
						}
					}
					String user_account = count32.substring(0, index);
					LocalInfo.setUserAccount(user_account);
					SLog.d(TAG, "getDeviceLocalInfo.setUserAccount == "
							+ LocalInfo.getUserAccount());

					byte[] byte8 = new byte[8];
					int len_version = dis.read(byte8);
					String version8 = new String(byte8, 0, len_version);
					int index_version = 0;
					for (int j = 0; j < version8.length(); j++) {
						if (0 == (version8.charAt(j))) {
							index_version = j;
							break;
						}
					}
					String version = version8.substring(0, index_version);
					LocalInfo.setVersion(version);
					SLog.d(TAG,
							"getDeviceLocalInfo.setVersion == "
									+ LocalInfo.getVersion());

					// 国家
					LocalInfo.setState(dis.readInt());
					SLog.d(TAG,
							"getDeviceLocalInfo.setState == "
									+ LocalInfo.getState());
					// 省
					LocalInfo.setProvince(dis.readInt());
					SLog.d(TAG, "getDeviceLocalInfo.setProvince == "
							+ LocalInfo.getProvince());
					// 市
					LocalInfo.setCity(dis.readInt());
					SLog.d(TAG,
							"getDeviceLocalInfo.setCity == "
									+ LocalInfo.getCity());
					// 区县
					LocalInfo.setStreet(dis.readInt());
					SLog.d(TAG,
							"getDeviceLocalInfo.setStreet == "
									+ LocalInfo.getStreet());
				}
			} else {
				try {
					socket.sendUrgentData(0xff);
				} catch (IOException e) {// 与CAE的socket连接中断，重试。。。
					// 远程把连接中断
					closeSocket();
					SLog.i(TAG, e.toString());
					return null;
				}
			}
		} catch (IOException e) {
			// 未知原因导致中断
			SLog.i(TAG, e.toString());
			return null;
		}
		return LocalInfo;
	}

	/**
	 * 解析包信息读取当前电视机的频道；4004700 成功 4004701 失败
	 * 
	 * @param cmdCaeAppGetTvIdRe
	 *            接受的命令字
	 * 
	 * @return 当前电视机的频道号
	 */
	public int getTvCurrentChannelId(int cmdCaeAppGetTvIdRe) {
		int channelId = -1;
		try {
			if (recevicePacket(cmdCaeAppGetTvIdRe)) {
				// 返回码
				responseCode = dis.readInt();// 4004300
				if (responseCode == GET_CURRENT_CHANNEL_SUC) {
					// 读频道id
					byte[] byte2 = new byte[2];
					dis.read(byte2);
					channelId = ConnectManager.bytesToShort(byte2);
				}
			}
		} catch (IOException e) {
			SLog.i(TAG, e.toString());
		} catch (Exception e) {
			SLog.i(TAG, e.toString());
		}
		return channelId;
	}

	/**
	 * 解析换台后的返回码，根据返回码不同，界面做出不同的响应
	 * 
	 * @param cmdSentChannelInfoRe
	 * 
	 * @return 0:读取换台信息失败 4004500:换台成功 4004501: 失败 ,此频道不存在 4004502:CRB 未插入，或者
	 *         CRB 故障 ， 提示用户插入或者重新插 拔 CRB 4004503 :未设置 CRB
	 */
	public int getChangeChannelSate(int cmdSentChannelInfoRe) {
		int responseCode = 0;
		// 解析包头的消息ID,根据这个id来判断是哪一个消息的返回值，并且解析响应的数据
		try {
			if (recevicePacket(cmdSentChannelInfoRe)) {
				// 解析包体，即返回码
				responseCode = dis.readInt();
			}
		} catch (IOException e) {
			SLog.i(TAG, e.toString());
			responseCode = 0;
		}
		return responseCode;
	}

	/**
	 * 解析包头数据，根据消息ID获取频道和频道号列表。 连接成功的时候返回码为CAE返回的响应，而连接不成功则返回-200，表示socket连接有误。
	 * 
	 * @param cmdCaeAppGetAudioRateRe
	 * 
	 * @throws Exception
	 */
	public ChannelPackage getTvChannelPagekage(int cmdCaeAppGetAudioRateRe) {
		ChannelPackage channelPagekage = null;
		try {
			if (recevicePacket(cmdCaeAppGetAudioRateRe)) {
				channelPagekage = new ChannelPackage();
				// 保存返回码
				responseCode = dis.readInt();
				SLog.d(TAG, "getTvChannelPagekage.return_code == "
						+ responseCode);
				channelPagekage.setReturnCode(responseCode);
				// 保存频道list
				channelPagekage.setChannels(readFavBody());
			}
		} catch (IOException e) {
			SLog.i(TAG, e.toString());
		} catch (Exception e) {
			SLog.i(TAG, e.toString());
		}
		return channelPagekage;
	}

	private boolean recevicePacket(int cmdCaeAppGetTvIdRe) throws IOException {
		boolean haveData;
		// 读命令字
		int cmd = dis.readInt();
		SLog.d(TAG, "socketpackte.cmd == " + cmd);
		if (cmd == cmdCaeAppGetTvIdRe) {
			// 消息序列号
			dis.readInt();
			// 保留字段
			dis.readInt();
			// 标识包体数据长度。不包含包头的长度。
			if (dis.readInt() > 0) {
				haveData = true;
			} else {
				haveData = false;
			}
		} else {
			haveData = false;
		}
		return haveData;
	}

	/**
	 * 读取最喜爱五个频道的包体的内容
	 * 
	 * @param channels
	 * @param dis
	 * @param return_code
	 * @throws IOException
	 * @throws Exception
	 */
	private ArrayList<TVChannel> readFavBody() throws IOException {
		ArrayList<TVChannel> channels = new ArrayList<TVChannel>();
		// 根据返回码判断是否成功4006000表示成功

		// 获取频道个数
		int channel_account = dis.readInt();
		SLog.d(TAG, "getTvChannelPagekage.channel_account == "
				+ channel_account);
		TVChannel fav;
		// 循环获取频道数据对象
		for (int i = 0; i < channel_account; i++) {
			fav = new TVChannel();
			int play_time; // 观看时常

			// 读频道id
			byte[] byte2 = new byte[2];
			dis.read(byte2);
			int channel_id = ConnectManager.bytesToShort(byte2);
			fav.setChannel_id(channel_id);
			SLog.d(TAG, "getTvChannelPagekage.channel_id == " + channel_id);

			// 读名字id
			byte[] byte64 = new byte[64];
			int len = dis.read(byte64);
			if (len > 0) {
				String gettedName64 = new String(byte64, 0, len);
				int index = 0;
				for (int j = 0; j < gettedName64.length(); j++) {
					if (0 == (gettedName64.charAt(j))) {
						index = j;
						break;
					}
				}
				String channelName = gettedName64.substring(0, index);
				fav.setChannel_name(channelName);
				SLog.d(TAG, "getTvChannelPagekage.channelName == "
						+ channelName);
			}

			// 读出播放时间
			play_time = dis.readInt();
			fav.setPlay_time(String.valueOf(play_time));
			SLog.d(TAG, "getTvChannelPagekage.play_time == " + play_time);
			// 添加到集合频道
			channels.add(fav);
			// 读保留位
			dis.readInt();
			SLog.d(TAG, "======================================");
		}

		return channels;
	}
}
